package com.youtills.core.io;

import com.youtills.core.lang.StringUtil;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;

/**
 * Created by IntelliJ IDEA.
 * User: ravi
 * Date: 9/5/12
 * Time: 7:45 PM
 * To change this template use File | Settings | File Templates.
 */
public class IoUtil {

    /** Key to find user home directory. */
    public static final String SYSTEM_PROPERTY_USER_HOME = "user.home";

    /** User Home directory. */
    public static final String USER_HOME_DIRECTORY = System.getProperty(
            SYSTEM_PROPERTY_USER_HOME);

    /** Prefix for class path file names. */
    public static final String FILE_CLASSPATH_PREFIX = "classpath:";

    /** Prefix for absolute file path. */
    public static final String FILE_PREFIX = "file:";

    /** classpath prefix replacer */
    private static final String FILE_CLASSPATH_PREFIX_REPLACER =
        StringUtil.EMPTY;

    /** file prefix replacer */
    private static final String FILE_PREFIX_REPLACER = StringUtil.EMPTY;

    /** Class loader */
    private static final ClassLoader CLASS_LOADER = IoUtil.class.getClassLoader();


    /**
     * Gets input stream for given filename. It calls getInputStream(fileName,
     * false); see #getInputStream(String fileName, boolean lookAtUserDir);
     *
     * @param   fileName  filename to get input stream.
     *
     * @return  Input stream for given file name. Returns <code>null</code> if file
     *          not found.
     */
    public static InputStream getInputStream(String fileName) {
        return getInputStream(fileName, false);
    }

    /**
     * Gets input stream for given file. if filename has prefix "classpath:" it will
     * look for file in the class path only.
     * If file name has prefix "file:" it will treat file name as full path and look for file.
     *
     * If the filename don't have "classpath:" or "file:" prefix
     * then If looks for file in user home directory if lookAtUserDir param is true.
     * Still not found then it looks for file in the class path if not found then
     * treats the filename as absolute path and tries to find it

     * <p>example 1: IoUtil.getInputStream("classpath:global.properties"); The above
     * call explicitly looks for file in the classpath only.</p>
     *
     * <p>example 2:IoUtil.getInputStream("global.properties"); First it will tries
     * to find global.properties in the classpath if din't find it then it will look for
     * global.properties.</p>
     *
     * @param   fileName       filename to get input stream.
     * @param   lookAtUserDir  if true looks for file in user directory too.
     *
     * @return  InputStream if it's find the file. If file not found then it will
     *          return <code>null</code>
     */
    public static InputStream getInputStream(String fileName,
            boolean lookAtUserDir) {
        IoInputStream stream = getIoStream(fileName, lookAtUserDir);
        if(stream != null){
            return stream.getInputStream();
        }
        return null;
    }

    public static IoInputStream getIoStream(String fileName){
        return getIoStream(fileName, false);
    }
    public static IoInputStream getIoStream(String fileName,
            boolean lookAtUserDir) {

        if (fileName.startsWith(FILE_CLASSPATH_PREFIX)) {
            return getIoStreamFromClassPath(fileName.replaceFirst(
                        FILE_CLASSPATH_PREFIX, FILE_CLASSPATH_PREFIX_REPLACER));
        } else if (fileName.startsWith(FILE_PREFIX)) {
            return getIoStreamFromFile(fileName.replaceFirst(FILE_PREFIX,
                        FILE_PREFIX_REPLACER));
        } else {
            // First look in classpath.
            IoInputStream stream= getIoStreamFromClassPath(fileName);
            // treat file as full path and look in the file system for file.
            if (stream == null) {
                stream = getIoStreamFromFile(fileName);
            }
            // Look in user directory if mentioned.
            if (stream == null && lookAtUserDir) {
                stream = getIoStreamFromUserDirectory(fileName);
            }
            return stream;
        }
    }

    /**
     * Gets Input Stream from User Directory Only.
     *
     * @param   fileName  file name to get Input Stream. It shold be relative path to
     *                    user home directory.
     *
     * @return  InputStream for given file name. returns <code>null</code> if file
     *          don't find.
     */
    public static InputStream getInputStreamFromUserDirectory(String fileName) {
        return getIoStreamFromUserDirectory(fileName).getInputStream();
    }
    public static IoInputStream getIoStreamFromUserDirectory(String fileName) {

        try {
            File file = new File(USER_HOME_DIRECTORY, fileName);
            return new IoInputStream(file.getAbsolutePath(), new FileInputStream(file));
        } catch (FileNotFoundException e) {
            return null;
        }
    }

    /**
     * Gets the InputStream Object from classpath for given file.
     *
     * @param   fileName name of the file to get stream.
     *
     * @return  InputStream
     */
    public static InputStream getInputStreamFromClassPath(String fileName) {
        IoInputStream stream = getIoStreamFromClassPath(fileName);
        if(stream != null){
            return stream.getInputStream();
        }
        return null;
    }

    public static IoInputStream getIoStreamFromClassPath(String fileName) {
        if(CLASS_LOADER.getResource(fileName) != null){
            return new IoInputStream(CLASS_LOADER.getResource(fileName).getPath(), CLASS_LOADER.getResourceAsStream(fileName));
        }
        //Second Try with all lower case letter
        if(CLASS_LOADER.getResource(fileName.toLowerCase()) != null){
            return new IoInputStream(CLASS_LOADER.getResource(fileName).getPath(), CLASS_LOADER.getResourceAsStream(fileName));
        }
        //Third Try with all upper case letter
        if(CLASS_LOADER.getResource(fileName.toUpperCase()) != null){
            return new IoInputStream(CLASS_LOADER.getResource(fileName).getPath(), CLASS_LOADER.getResourceAsStream(fileName));
        }
        return null;
    }

    /**
     * Get input stream from file.
     *
     * @param   filePath full path of the file to get inputstream.
     *
     * @return  InputStream.
     */
    public static InputStream getInputStreamFromFile(String filePath) {
        IoInputStream iStream = getIoStreamFromFile(filePath);
        if (iStream != null){
            return iStream.getInputStream();
        }
        return null;
    }

    public static IoInputStream getIoStreamFromFile(String filePath) {

        File file = new File(filePath);

        if (file.exists() && file.isFile()) {

            try {
                return new IoInputStream(file.getAbsolutePath(), new FileInputStream(file));
            } catch (FileNotFoundException e) {
                return null;
            }
        }

        return null;
    }
}